package net.guruqu.rvoView;

import java.awt.Canvas;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.HashMap;

import com.gibbon.jme.context.JmeContext;
import com.gibbon.jme.context.RenderPass;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.light.DirectionalLight;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Line;
import com.jme.scene.Node;
import com.jme.scene.SharedNode;
import com.jme.scene.Line.Mode;
import com.jme.scene.Spatial.LightCombineMode;
import com.jme.scene.Spatial.TextureCombineMode;
import com.jme.scene.state.LightState;
import com.jme.scene.state.ZBufferState;
import com.jme.scene.state.ZBufferState.TestFunction;
import com.jme.system.DisplaySystem;

/**
 * @author Jasper
 * 
 */
public class GroundLevelView {

	class GroundRenderPass extends RenderPass {
		float aspectRatio;
		float centerX, centerY;
		DisplaySystem display;
		float fieldOfView;
		Line horizone;

		KeyInputAdapter keyAdapter;
		MouseInputAdapter mouseAdapter;
		Node root;

		public GroundRenderPass() {
			fieldOfView = 100;
			aspectRatio = 640f / 480f;
			centerX = 0;
			centerY = 0;
			mouseAdapter = new MouseInputAdapter(new InputAction() {
				public boolean drag0 = false;
				public float lastX = -1, lastY = -1;

				@Override
				public void performAction(InputActionEvent evt) {
					// TODO Auto-generated method stub
					// System.out.println("Drag:"+drag0);
					float newX = -5, newY = -5;
					if (evt.getTriggerName().startsWith("X")) {
						newX = evt.getTriggerPosition();
						newY = lastY;
					} else if (evt.getTriggerName().startsWith("Y")) {
						newY = evt.getTriggerPosition();
						newX = lastX;
					} else {
						newX = lastX;
						newY = lastY;
					}
					// System.out.println("X:" + newX + "Y:" + newY);
					if (evt.getTriggerName().startsWith("MOUSE0")) {
						if (evt.getTriggerPressed()) {
							drag0 = true;
						} else {
							drag0 = false;
						}
					}
					if (drag0) {
						setCenterX(getCenterX() - getSpanX(newX - lastX));
						setCenterY(getCenterY() + getSpanY(newY - lastY));
					}
					lastX = newX;
					lastY = newY;
				}
			});
			keyAdapter = new KeyInputAdapter(new InputAction() {
				public void performAction(InputActionEvent evt) {
					// TODO Auto-generated method stub

				}
			});
		}

		@Override
		protected void doRender(JmeContext context) {
			// TODO Auto-generated method stub
			DisplaySystem display = DisplaySystem.getDisplaySystem();
			Renderer renderer = display.getRenderer();
			renderer.clearColorBuffer();
			renderer.clearZBuffer();
			renderer.draw(root);

			if (renderer.isInOrthoMode()) {
				renderer.draw(horizone);
			} else {
				renderer.setOrtho();
				renderer.draw(horizone);
				renderer.unsetOrtho();
			}
		}

		@Override
		protected void doUpdate(JmeContext context) {
			// TODO Auto-generated method stub
			try {
				mouseAdapter.update();
				if (cameraNeedUpdate)
					updateCameraLocal();

				horizone.setLocalTranslation(
						context.getRenderer().getWidth() / 2f, context
								.getRenderer().getHeight() / 2f, 0);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		public float getAspectRatio() {
			return aspectRatio;
		}

		public float getCenterX() {
			return centerX;
		}

		public float getCenterY() {
			return centerY;
		}

		public float getFieldOfView() {
			return fieldOfView;
		}

		public KeyInputAdapter getKeyAdapter() {
			return keyAdapter;
		}

		public MouseInputAdapter getMouseAdapter() {
			return mouseAdapter;
		}

		public float getRealX(float x) {
			Camera camera = DisplaySystem.getDisplaySystem().getRenderer()
					.getCamera();
			float width = DisplaySystem.getDisplaySystem().getRenderer()
					.getWidth();

			float v1 = camera.getFrustumLeft();
			float v2 = camera.getFrustumRight();
			float xoff = camera.getLocation().x;
			float s = (v2 - v1) * x + v1 + xoff;

			return s;
		}

		public float getRealY(float y) {
			Camera camera = DisplaySystem.getDisplaySystem().getRenderer()
					.getCamera();
			float height = DisplaySystem.getDisplaySystem().getRenderer()
					.getHeight();

			float v1 = camera.getFrustumTop();
			float v2 = camera.getFrustumBottom();
			float offy = camera.getLocation().y;
			float s = (v2 - v1) * y + v1 + offy;
			return s;
		}

		public float getSpanX(float span) {

			Camera camera = DisplaySystem.getDisplaySystem().getRenderer()
					.getCamera();
			float width = DisplaySystem.getDisplaySystem().getRenderer()
					.getWidth();

			float v1 = camera.getFrustumLeft();
			float v2 = camera.getFrustumRight();

			float s = (v2 - v1) * span;
			return s;
		}

		public float getSpanY(float span) {
			Camera camera = DisplaySystem.getDisplaySystem().getRenderer()
					.getCamera();
			float height = DisplaySystem.getDisplaySystem().getRenderer()
					.getHeight();

			float v1 = camera.getFrustumTop();
			float v2 = camera.getFrustumBottom();

			float s = (v2 - v1) * span;
			return s;
		}

		@Override
		public void initPass(JmeContext context) {
			display = DisplaySystem.getDisplaySystem();
			Camera camera = display.getRenderer().createCamera(640, 480);
			camera.setFrustum(1, 1e4f, -aspectRatio * fieldOfView / 2f,
					aspectRatio * fieldOfView / 2f, -fieldOfView / 2f,
					fieldOfView / 2f);
			camera.setParallelProjection(true);
			// camera.setFrustumPerspective(45, 640.0f / 480.0f, 1, 1000);
			Vector3f loc = new Vector3f(centerX, centerY, 900f);
			camera.setLocation(loc);
			camera.setUp(new Vector3f(0, 1, 0));
			camera.setDirection(new Vector3f(0, 0, -1));
			camera.update();

			display.getRenderer().setCamera(camera);
			display.getRenderer().setBackgroundColor(ColorRGBA.gray);

			root = new Node("root");

			ZBufferState bufferState = display.getRenderer()
					.createZBufferState();
			bufferState.setEnabled(true);
			bufferState.setFunction(ZBufferState.TestFunction.LessThan);
			root.setRenderState(bufferState);
			Node node = null;
			node = (Node) modelRepository.getRepository().get("env");
			SharedNode sn = new SharedNode(node);
			root.attachChild(sn);

			LightState lightState = display.getRenderer().createLightState();
			DirectionalLight directionalLight = new DirectionalLight();
			directionalLight.setDiffuse(new ColorRGBA(0.6f, 0.6f, 0.6f, 1));
			directionalLight.setDirection(new Vector3f(-0.577f, -0.577f,
					-0.577f));
			directionalLight.setEnabled(true);
			lightState.setGlobalAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1));
			lightState.attach(directionalLight);
			lightState.setEnabled(true);

			root.setRenderState(lightState);
			root.updateRenderState();

			int w, h;
			w = display.getRenderer().getWidth();
			h = display.getRenderer().getHeight();
			Vector3f v3f[] = new Vector3f[] { new Vector3f(-w / 2 - 1, 0, 0),
					new Vector3f(w / 2 + 1, 0, 0) };
			ColorRGBA c3f[] = new ColorRGBA[] { new ColorRGBA(0, 1, 0, 0.6f),
					new ColorRGBA(0, 1, 0, 0.6f) };
			horizone = new Line("Horizon Line", v3f, null, c3f, null);
			horizone.setLightCombineMode(LightCombineMode.Off);
			horizone.setTextureCombineMode(TextureCombineMode.Off);
			ZBufferState bufferState2 = display.getRenderer()
					.createZBufferState();
			bufferState2.setFunction(TestFunction.Always);
			horizone.setLocalTranslation(w / 2, h / 2, 0);
			horizone.setRenderState(bufferState2);
			horizone.setMode(Mode.Segments);
			horizone.setLineWidth(2);
			horizone.updateModelBound();
			horizone.updateRenderState();
			// root.attachChild(horizone);
			root.updateModelBound();
			canvasInstance.repaint();
		}

		public void resizeCanvas(int w, int h) {
			setAspectRatio((float) w / (float) h);
			canvasInstance.repaint();
		}

		public void setAspectRatio(float aspectRatio) {
			this.aspectRatio = aspectRatio;
			updateCamera();
		}

		public void setCenterX(float centerX) {
			this.centerX = centerX;
			updateCamera();
		}

		public void setCenterY(float centerY) {
			this.centerY = centerY;
			updateCamera();
		}

		public void setFieldOfView(float fieldOfView) {
			this.fieldOfView = fieldOfView;
			updateCamera();
		}

		boolean cameraNeedUpdate;

		public void updateCamera() {
			cameraNeedUpdate = true;
		}

		private void updateCameraLocal() {
			Camera camera = display.getRenderer().getCamera();
			Vector3f loc = new Vector3f(centerX, centerY, 900f);
			camera.setLocation(loc);
			camera.setFrustum(1, 1e4f, -aspectRatio * fieldOfView / 2f / zoom,
					aspectRatio * fieldOfView / 2f / zoom, -fieldOfView / 2f
							/ zoom, fieldOfView / zoom / 2f);
			camera.update();
			cameraNeedUpdate = false;

		}
	}

	Canvas canvasInstance;

	ModelRepository modelRepository;

	GroundRenderPass myRenderPass;

	HashMap<String, String> properties;

	float zoom;

	public GroundLevelView(HashMap<String, String> prop,
			ModelRepository modelRepository) {
		this.properties = prop;
		this.modelRepository = modelRepository;
		zoom = 1f;
	}

	public void setCanvas(JmeContext context) {
		if (canvasInstance != null)
			throw new RuntimeException("Canvas Instance already set!");
		Canvas canvas = context.getCanvas();
		canvasInstance = canvas;
		myRenderPass = new GroundRenderPass();
		context.getPassManager().add(myRenderPass);
		canvasInstance.addComponentListener(new ComponentAdapter() {
			@Override
			public void componentResized(ComponentEvent ce) {
				myRenderPass.resizeCanvas(canvasInstance.getWidth(),
						canvasInstance.getHeight());
			}
		});
		myRenderPass.getMouseAdapter().setup(canvasInstance);
		canvas.repaint();
	}

	public float getHorizoneValue() {
		return myRenderPass.getCenterY();
	}

	public HashMap<String, String> getProperties() {
		return properties;
	}

	public float getZoom() {
		return zoom;
	}

	public void setHorizoneValue(float h) {
		myRenderPass.setCenterY(h);
	}

	public void setProperties(HashMap<String, String> properties) {
		this.properties = properties;
	}

	public void setZoom(float zoom) {
		this.zoom = zoom;
		myRenderPass.updateCamera();
	}

}
